package genetica.cromosomas;

import genetica.cromosomas.fenotipos.Fenotipo;
import genetica.cromosomas.fenotipos.FenotipoViajante;
import genetica.cromosomas.genes.Gen;
import genetica.cromosomas.genes.GenNaturalLimitado;
import genetica.data.viajante.Distancias;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
import java.util.TreeSet;

public class CromosomaViajante extends Cromosoma implements Cloneable,
		GenesConAfinidad {
	// estatico porque siempre hay las mismas ciudades y solo necesitamos
	// crearlas una vez
	public static Integer tam = null;
	protected static Gen[][] ciudadesProximas;
	static {
		ciudadesProximas = new Gen[Distancias.numCiudades()][Distancias
				.numCiudades() - 1];
		Comparator<Integer> comparador = new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				Random r = new Random();
				int result = o1.compareTo(o2);
				if (result == 0) {
					if (r.nextBoolean()) {
						return 1;
					}
					return -1;
				}
				if (Cromosoma.maximizar)
					return result;
				return -result;
			}
		};
		for (int i = 0; i < Distancias.numCiudades(); i++) {

			TreeMap<Integer, Gen> arbol = new TreeMap<Integer, Gen>(comparador);

			for (int j = 0; j < Distancias.numCiudades(); j++) {
				if (i != j) {
					arbol.put(Distancias.getDist(i, j), new GenNaturalLimitado(
							j, Distancias.numCiudades()));
				}
			}
			int j = 0;
			while (arbol.size() > 0) {

				ciudadesProximas[i][j++] = arbol.pollFirstEntry().getValue();
			}
		}
	}

	@SuppressWarnings("unchecked")
	public CromosomaViajante(CromosomaViajante other) {
		// NORMAL
		super();
		this.aptitud = other.aptitud;
		this.genes = (ArrayList<Gen>) other.genes.clone();
		this.punt_acumu = other.punt_acumu;
		this.fenotipo = (ArrayList<Fenotipo>) other.fenotipo.clone();
		this.puntuacion = other.puntuacion;

		// PROPIO
	}

	public CromosomaViajante(int ciudadInicial) { // falta pasarle el evaluador
													// cuando lo
		// el super crea longGenes, genes y fenotipos
		super(); // tengamos
		if (tam == null) {
			tam = Distancias.numCiudades();
		}
		// log²(nCiudades)
		// int ciudadesRestantes = Distancias.numCiudades();
		// ceil == el techo del valor
		Random r = new Random();
		ArrayList<GenNaturalLimitado> ciudades = new ArrayList<GenNaturalLimitado>();
		for (int i = 0; i < Distancias.numCiudades(); i++) {
			ciudades.add(new GenNaturalLimitado(i, Distancias.numCiudades()));
		}
		while (ciudades.size() != 0) {
			GenNaturalLimitado g = ciudades.remove(r.nextInt(ciudades.size()));
			genes.add(g);
		}

	}

	@Override
	public void calcularFenotipo() {
		// TODO Auto-generated method stub
		this.fenotipo.clear();
		this.fenotipo.add((Fenotipo) new FenotipoViajante(genes));
	}

	@Override
	public CromosomaViajante clone() {
		return new CromosomaViajante(this);
	}

	@Override
	public String toString() {
		String cadena = "";

		cadena += "Distancia: " + this.aptitud + "; ";

		for (int i = 0; i < genes.size(); i++)
			cadena += ((GenNaturalLimitado) genes.get(i)).getGenValue() + ", ";
		return cadena;
	}

	@Override
	public void codifica() {
		boolean[] visitada = new boolean[genes.size()];
		ArrayList<Gen> nuevosGenes = new ArrayList<Gen>();
		for (int i = 0; i < genes.size(); i++) {
			visitada[i] = false;
		}
		for (int i = 0; i < genes.size(); i++) {
			GenNaturalLimitado gen = (GenNaturalLimitado) genes.get(i);
			int pos = 0;
			// si esta visitada no hay que tenerla en cuenta
			for (int j = 0; j < gen.getGenValue(); j++) {
				if (!visitada[j]) {
					pos++;
				}
			}
			visitada[gen.getGenValue()] = true;
			nuevosGenes.add(new GenNaturalLimitado(pos, gen.getMax()));
		}
		this.genes = nuevosGenes;

	}

	@Override
	public void decodifica() {
		boolean[] visitada = new boolean[genes.size()];
		ArrayList<Gen> nuevosGenes = new ArrayList<Gen>();
		for (int i = 0; i < genes.size(); i++) {
			visitada[i] = false;
		}
		for (int i = 0; i < genes.size(); i++) {
			GenNaturalLimitado gen = (GenNaturalLimitado) genes.get(i);
			int pos = 0;
			for (int j = 0; j < gen.getGenValue(); j++) {
				// si esta visitada es una ciudad mas a la derecha
				// (incrementamos en una)
				while (visitada[pos]) {
					pos++;
				}
				pos++;
			}
			while (visitada[pos]) {
				pos++;
			}
			visitada[pos] = true;
			nuevosGenes.add(new GenNaturalLimitado(pos, gen.getMax()));
		}
		this.genes = nuevosGenes;
	}

	@Override
	public Gen[] genesProximos(int pos) {
		GenNaturalLimitado gen = (GenNaturalLimitado) this.getGenes().get(pos);
		return CromosomaViajante.ciudadesProximas[gen.getGenValue()];

	}
}
